package drds.plus.sql_process.optimizer.chooser;

import drds.plus.common.properties.ConnectionProperties;
import drds.plus.sql_process.abstract_syntax_tree.configuration.IndexMapping;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.BooleanFilter;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.Filter;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.Operation;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.OrsFilter;
import drds.plus.sql_process.abstract_syntax_tree.node.query.Query;
import drds.plus.sql_process.abstract_syntax_tree.node.query.TableQuery;
import drds.plus.sql_process.utils.DnfFilters;
import drds.plus.sql_process.utils.Filters;
import drds.plus.sql_process.utils.OptimizerUtils;
import drds.plus.util.ExtraCmd;

import java.util.*;

/**
 * 条件分离器 简单来说 一个查询 A = 1 and B = 2 <br/>
 * 需要分析出哪些是key filter?哪些是 post filter( ResultFilter) <br/>
 * 在这里进行分离 columnName filter就是能走索引的那些filter，post filter就是必须遍历结果集的那些filter
 */
public class FilterSpliter {

    /**
     * 根据where中的所有条件按照or进行分隔，生成多个query请求. (主要考虑部分存储引擎不支持or语法)
     */
    public static List<Query> splitByOr(TableQuery tableQueryNode, Map<String, Object> extraCmd) {
        if (tableQueryNode.getWhere() == null) {
            return new LinkedList<Query>();
        }

        if (!DnfFilters.isDnf(tableQueryNode.getWhere())) {
            throw new IllegalArgumentException("not dnf!!! fuck!!\n" + tableQueryNode.getWhere());
        }

        List<List<Filter>> filterListList = DnfFilters.toDnfFilterListList(tableQueryNode.getWhere());
        // 判断是否要处理OR转化为index sort，比如mysql可以push
        // 比如hbase/bdb这一类，OR条件目前只能是主键索引，由执行器层面去解决
        // 如果涉及多字段时就需要拆分为index sort，同时做uniq
        boolean needCopy = (filterListList.size() > 1);
        List<Query> queryList = new LinkedList<Query>();
        if (isOptimizeIndexMerge(extraCmd) || filterListList.size() == 1) {
            for (List<Filter> filterList : filterListList) {
                List columnList = Arrays.asList(Filters.toColumnToFilterListMap(filterList).keySet().toArray());
                String tableName = tableQueryNode.getTableName();
                IndexMapping indexMapping = IndexChooser.findBestIndexMetaData(tableQueryNode.getTableMetaData(), columnList, filterList, tableName, extraCmd);
                if (indexMapping == null) {
                    indexMapping = tableQueryNode.getTableMetaData().getPrimaryKeyIndexMetaData();
                }
                // 如果找不到对应条件的主键，可能需要全表扫描
                TableQuery subQueryTableQueryNode = tableQueryNode;
                if (needCopy) {
                    subQueryTableQueryNode = tableQueryNode.deepCopy();
                }

                subQueryTableQueryNode.setIndexMappingUsed(indexMapping);
                if (indexMapping == null) {// 如果无主键进行全表扫描
                    subQueryTableQueryNode.setFullTableScan(true);
                } else {
                    subQueryTableQueryNode.setWhereAndSetNeedBuild(null);// 清空where
                }

                Map<FilterType, Filter> filterTypeToFilterMap = splitByIndex(filterList, subQueryTableQueryNode);
                subQueryTableQueryNode.setIndexQueryKeyFilterAndSetNeedBuild(filterTypeToFilterMap.get(FilterType.IndexQueryKeyFilter));
                subQueryTableQueryNode.setIndexQueryValueFilter(filterTypeToFilterMap.get(FilterType.IndexQueryValueFilter));
                subQueryTableQueryNode.setResultFilterAndSetNeedBuild(filterTypeToFilterMap.get(FilterType.ResultFilter));
                queryList.add(subQueryTableQueryNode);
            }
        } else {
            tableQueryNode.setIndexMappingUsed(tableQueryNode.getTableMetaData().getPrimaryKeyIndexMetaData());// 默认选中主键
            tableQueryNode.setResultFilterAndSetNeedBuild(tableQueryNode.getWhere());
        }

        return queryList;
    }

    /**
     * 将一组filter，根据索引信息拆分为key/indexValue/Value几种分组，keyFilter可以下推到叶子节点减少数据返回
     */
    public static Map<FilterType, Filter> splitByIndex(List<Filter> $filterList, TableQuery tableQueryNode) {
        Map<FilterType, Filter> filterTypeToFilterMap = new HashMap<FilterType, Filter>();
        Map<Object, List<Filter>> columnToFilterListMap = Filters.toColumnToFilterListMap($filterList);
        IndexMapping indexMapping = tableQueryNode.getIndexMappingUsed();
        if (indexMapping == null) { // 默认选择主键
            indexMapping = tableQueryNode.getTableMetaData().getPrimaryKeyIndexMetaData();
        }

        List<Item> indexKeyItemList = new ArrayList(0);
        List<Item> indexValueItemList = new ArrayList(0);
        if (indexMapping != null) {
            indexKeyItemList = OptimizerUtils.columnMetaListToIColumnList(indexMapping.getKeyColumnMetaDataList(), tableQueryNode.getTableName());
            indexValueItemList = OptimizerUtils.columnMetaListToIColumnList(indexMapping.getValueColumnMetaDataList(), tableQueryNode.getTableName());
        }
        List<Filter> indexQueryKeyFilterList = new LinkedList<Filter>();
        List<Filter> indexQueryValueFilterList = new LinkedList<Filter>();
        List<Filter> resultFilterList = new LinkedList($filterList);
        for (int i = 0; i < indexKeyItemList.size(); i++) {
            // 不等于,is null, is not null, like 应该按照valueFilter处理
            List<Filter> filterList = columnToFilterListMap.get(indexKeyItemList.get(i));
            if (filterList != null) {
                for (Filter filter : filterList) {
                    if (filter instanceof OrsFilter) {
                        // or条件不走key filter
                        indexQueryValueFilterList.add(filter);
                        continue;
                    }
                    // filter右边不是常量的，不能走索引
                    if (((BooleanFilter) filter).getValue() != null && (((BooleanFilter) filter).getValue() instanceof Item)) {
                        continue;
                    }
                    if (filter != null && //
                            filter.getOperation() != Operation.not_equal &&//
                            filter.getOperation() != Operation.is_null &&//
                            filter.getOperation() != Operation.is_not_null &&//
                            filter.getOperation() != Operation.like) {//
                        indexQueryKeyFilterList.add(filter);
                    } else {
                        indexQueryValueFilterList.add(filter);
                    }
                }
                filterList.clear();
            }
        }

        for (int i = 0; i < indexValueItemList.size(); i++) {
            // 不等于,is null, is not null, like 应该按照valueFilter处理
            List<Filter> filterList = columnToFilterListMap.get(indexValueItemList.get(i));
            if (filterList != null) {
                for (Filter filter : filterList) {
                    // filter右边不是常量的，不能走索引
                    if (filter instanceof BooleanFilter && ((BooleanFilter) filter).getValue() != null && (((BooleanFilter) filter).getValue() instanceof Item)) {
                        continue;
                    }

                    indexQueryValueFilterList.add(filter);
                }
                filterList.clear();
            }

        }
        //
        resultFilterList.removeAll(indexQueryKeyFilterList);
        resultFilterList.removeAll(indexQueryValueFilterList);
        Filter indexQueryKeyFilter = DnfFilters.andDnfFilterList(indexQueryKeyFilterList);
        Filter indexQueryValueFilter = DnfFilters.andDnfFilterList(indexQueryValueFilterList);
        Filter resultFilter = DnfFilters.andDnfFilterList(resultFilterList);
        filterTypeToFilterMap.put(FilterType.IndexQueryKeyFilter, indexQueryKeyFilter);
        filterTypeToFilterMap.put(FilterType.IndexQueryValueFilter, indexQueryValueFilter);
        filterTypeToFilterMap.put(FilterType.ResultFilter, resultFilter);
        return filterTypeToFilterMap;
    }

    private static boolean isOptimizeIndexMerge(Map<String, Object> extraCmd) {
        return ExtraCmd.getExtraCmdBoolean(extraCmd, ConnectionProperties.CHOOSE_INDEX_MERGE, false);
    }
}
